package cn.iocoder.yudao.module.trade.service.order;

import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.module.currency.api.rate.ExchangeRateApi;
import cn.iocoder.yudao.module.currency.api.rate.dto.ExchangeRateRespDTO;
import cn.iocoder.yudao.module.digital.api.cardkeymanager.dto.CardKeySecretDTO;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.system.api.mail.MailSendApi;
import cn.iocoder.yudao.module.system.api.mail.dto.MailSendSingleToUserReqDTO;
import cn.iocoder.yudao.module.trade.config.TradeOrderMailConfig;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.enums.OrderMailSceneEnum;
import com.alibaba.fastjson.JSON;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

/**
 * @author cai
 * @date 2024/03/21 17:01
 */
@Slf4j
@Service
public class OrderMailService {

    @Resource
    private MailSendApi mailSendApi;

    @Resource
    private ProductSkuApi productSkuApi;

    @Resource
    private ProductSpuApi productSpuApi;

    @Resource
    private ExchangeRateApi exchangeRateApi;

    @Resource
    private TradeOrderMailConfig tradeOrderMailConfig;

    /**
     * 发送订单邮件
     *
     * @param order      订单
     * @param itemDOList 订单项
     * @param userId     邮件模板所属用户
     * @param secretList 卡密
     */
    public void sendOrderCDKMail(TradeOrderDO order, List<TradeOrderItemDO> itemDOList, Long userId, List<CardKeySecretDTO> secretList) {
        if (!tradeOrderMailConfig.getCdkEnable()) {
            log.info("发送订单CDK邮件, 开关未开启");
            return;
        }
        // order-product-review
        OrderMailSceneEnum code = OrderMailSceneEnum.getByCode(tradeOrderMailConfig.getCdkTemplateCode());
        if (Objects.isNull(code)) {
            log.error("发送订单CDK邮件, cdk-template-code 配置不正确");
            return;
        }
        String orderNo = order.getNo();
        log.info("发送订单CDK邮件, 订单号: {}", orderNo);
        String cardSecret = convertSecretList2Str(secretList);
        itemDOList.stream().collect(Collectors.groupingBy(TradeOrderItemDO::getReceiveEmail)).forEach((receiveMail, itemList) -> {
            MailSendSingleToUserReqDTO req = new MailSendSingleToUserReqDTO();
            Set<Long> skuIds = itemList.stream().map(TradeOrderItemDO::getSkuId).collect(Collectors.toSet());
            String goodsName = productSkuApi.getSkuList(skuIds).stream().map(ProductSkuRespDTO::getName).distinct().collect(Collectors.joining(","));
            Map<String, Object> templateParams = MapUtil.<String, Object>builder()
                .put("orderNum", orderNo)
                .put("goodsName", goodsName)
                .put("cardDetail", cardSecret)
                .build();
            itemList.stream().map(TradeOrderItemDO::getCount).reduce(Integer::sum).ifPresent(sum -> {
                templateParams.put("buyNum", sum);
            });
            ExchangeRateRespDTO exchangeRate = exchangeRateApi.getExchangeRate(order.getPaymentCurrency());
            ExchangeRateRespDTO orderRate = exchangeRateApi.getExchangeRate(order.getOrderCurrency());
            itemList.stream().map(TradeOrderItemDO::getPayPrice).reduce(Integer::sum).ifPresent(sum -> {
                Integer value = exchangeRateApi.calculateWithRate(sum, orderRate.getExchangeRate());
                templateParams.put("price", exchangeRate.getCurrencySymbol() + BigDecimal.valueOf(value).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
            });
            Optional<TradeOrderItemDO> first = itemList.stream().findFirst();
            if (first.isPresent()) {
                TradeOrderItemDO item = first.get();
                Long spuId = item.getSpuId();
                ProductSpuRespDTO spu = productSpuApi.getSpu(spuId);
                if (Objects.nonNull(spu)) {
                    templateParams.put("productDesc", spu.getIntroduction());
                    templateParams.put("productDetail", spu.getDescription());
                }
            }

            req.setUserId(userId);
            req.setMail(receiveMail);
            req.setTemplateCode(code.getTemplateCode());
            req.setTemplateParams(templateParams);
            mailSendApi.sendSingleMailToAdmin(req);
        });
    }

    public void sendOrderDetailMail(TradeOrderDO order, List<TradeOrderItemDO> itemDOList, Long userId) {
        if (!tradeOrderMailConfig.getDetailEnable()) {
            log.info("发送订单详情邮件, 开关未开启");
            return;
        }
        // order-payment-review
        OrderMailSceneEnum code = OrderMailSceneEnum.getByCode(tradeOrderMailConfig.getDetailTemplateCode());
        if (Objects.isNull(code)) {
            log.error("发送订单详情邮件, detail-template-code 配置不正确");
            return;
        }
        String orderNo = order.getNo();
        log.info("发送订单详情邮件, 订单号: {}", orderNo);
        itemDOList.stream().collect(Collectors.groupingBy(TradeOrderItemDO::getReceiveEmail)).forEach((receiveMail, itemList) -> {
            MailSendSingleToUserReqDTO req = new MailSendSingleToUserReqDTO();
            Set<Long> skuIds = itemList.stream().map(TradeOrderItemDO::getSkuId).collect(Collectors.toSet());
            String goodsName = productSkuApi.getSkuList(skuIds).stream().map(ProductSkuRespDTO::getName).distinct().collect(Collectors.joining(","));
            Map<String, Object> templateParams = MapUtil.<String, Object>builder()
                .put("orderNum", orderNo)
                .put("goodsName", goodsName)
                .build();
            itemList.stream().map(TradeOrderItemDO::getCount).reduce(Integer::sum).ifPresent(sum -> {
                templateParams.put("buyNum", sum);
            });
            ExchangeRateRespDTO exchangeRate = exchangeRateApi.getExchangeRate(order.getPaymentCurrency());
            ExchangeRateRespDTO orderRate = exchangeRateApi.getExchangeRate(order.getOrderCurrency());
            itemList.stream().map(TradeOrderItemDO::getPayPrice).reduce(Integer::sum).ifPresent(sum -> {
                Integer value = exchangeRateApi.calculateWithRate(sum, orderRate.getExchangeRate());
                templateParams.put("price", exchangeRate.getCurrencySymbol() + BigDecimal.valueOf(value).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
            });

            req.setUserId(userId);
            req.setMail(receiveMail);
            req.setTemplateCode(code.getTemplateCode());
            req.setTemplateParams(templateParams);
            mailSendApi.sendSingleMailToAdmin(req);
        });
    }

    public void sendManualOrderMail(List<TradeOrderDO> orders) {
        if (!tradeOrderMailConfig.getManualEnable()) {
            log.info("发送未人工处理订单提醒邮件, 开关未开启");
            return;
        }
        // order-untreated-warning
        OrderMailSceneEnum code = OrderMailSceneEnum.getByCode(tradeOrderMailConfig.getManualOrderTemplateCode());
        if (Objects.isNull(code)) {
            log.error("发送订单邮件, manual-order-template-code 配置不正确");
            return;
        }
        Map<String, Object> templateParams = MapUtil.<String, Object>builder()
            .build();
        MailSendSingleToUserReqDTO req = new MailSendSingleToUserReqDTO();
        req.setUserId(1L);
        req.setMail(tradeOrderMailConfig.getManualOrderReceiver());
        req.setTemplateCode(code.getTemplateCode());
        req.setTemplateParams(templateParams);
        mailSendApi.sendSingleMailToAdmin(req);
    }

    private String convertSecretList2Str(List<CardKeySecretDTO> secretList) {
        log.info("卡密: {}", JSON.toJSONString(secretList));
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < secretList.size(); i++) {
            CardKeySecretDTO secret = secretList.get(i);
            int index = i + 1;
            String s = concatSecret(secret, index);
            builder.append(s);
        }
        return builder.toString();
    }

    private String concatSecret(CardKeySecretDTO secret, int index) {
        List<KeyValue<String, String>> rows = new ArrayList<>();
        if (StringUtils.isNotBlank(secret.getCardNumber())) {
            rows.add(new KeyValue<>("Serial No", secret.getCardNumber()));
        }
        if (StringUtils.isNotBlank(secret.getCode())) {
            rows.add(new KeyValue<>("Code", secret.getCode()));
        }
        if (StringUtils.isNotBlank(secret.getPin())) {
            rows.add(new KeyValue<>("PIN", secret.getPin()));
        }
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < rows.size(); i++) {
            KeyValue<String, String> row = rows.get(i);
            builder.append("<tr>");
            if (i == 0) {
                builder.append("<td rowspan=\"" + rows.size() + "\" style=\"border:1px solid #F1F1F1;width:30px;padding:9px 0px;color:#999999;text-align:center;\">" + index + "</td>");
            }
            builder.append("<td style=\"border:1px solid #F1F1F1;padding:9px 0px;width:90px;text-align:right;padding-right:16px;\">" + row.getKey() + ":</td>");
            builder.append("<td style=\"border:1px solid #F1F1F1;padding:9px 0px;color:#333333;padding-left:12px;\">" + row.getValue() + "</td>");
            builder.append("</tr>");
        }
        return builder.toString();
    }

}
